/*
 Készítsen olyan select-es szervert, ami a következőképp működik:

 MAXCLIENT-ben definiált maximális számú klienssel összeköttetést képes létesíteni
 A klienseket láncolt listában tárolja
 Kilépő klienseket helyesen lekezeli, a láncolt listából törli
 Mikor egy kliens csatlakozott, a szerver kér tőle öt egész számot 1-90 közt “a,b,c,d,e” formátumban,
 közben az 1-től 90-ig tartó egész számok halmazából véletlenszerűen kiválaszt 5 db-ot.
 Mikor a klienstől megkapta a kliens tippjeit, visszaküldi a találatok számát,
 a kisorsolt számokat és a kliens tippjeit. Majd felkínálja egy újabb játék lehetőségét,
 mely az újonnan csatlakozott klienseknél alkalmazott folyamattal azonos.

 +1 jegy:
 Parancssori argumentumként megadható két pozitív egész szám,
 mely az egy körben megteendő tippek számát és egy tipp felső határát jelenti (ez az alapesetben 5 és 90).
 Ezeket a paramétereket játék előtt a klienssel is közölje!

 + 1 jegy:
 Minden megkapott tipp esetén a szerver saját magának írja ki, hogy az adott tipp milyen IP-ről és portról jött.

 + 1 jegy:
 Ha a szerver a „STAT” parancsot kapja meg, készítsen statisztikát az indulása óta folyó játékokról:
 mely számot hányszor sorsolta ki a szerver, illetve mely számot hányszor tették meg tippként a kliensek.
 A 0-tól különböző értékeket csökkenő sorrendben küldje el a kliensnek.

 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
#else
#include <netdb.h>
#endif

#define MAXC 2
#define PORT "2222"

struct kliens {
	int fd;
	struct kliens* next;
	int* tipp;
	int* huzott;
	char ip[200];
	char port[20];
};
struct kliens* kliensek = NULL;
int kliensszam = 0;

fd_set master;
int fdmax;
int listenSocket;

int tippszam = 5;
int korlat = 90;

int* tippeltek;
int* huzottak;

void elutasit(int ujfd) {
	char uz[200];
	memset(uz, '\0', 200);
	strcpy(uz, "\n\nMAXclient!\n\n");
	send(ujfd, uz, strlen(uz), 0);
#ifdef _WIN32
	closesocket(ujfd);
#else
	close(ujfd);
#endif
}

void elfogad(int ujfd, struct sockaddr sa) {
	char uz[200];
	struct kliens* kl = (struct kliens*) malloc(sizeof(struct kliens));
	memset(uz, '\0', 200);

#ifdef _WIN32
	sprintf(kl->ip,"%s",inet_ntoa( ((struct sockaddr_in*)&sa)->sin_addr) );
#else
	inet_ntop(sa.sa_family, &(((struct sockaddr_in*) &sa)->sin_addr), kl->ip,
			INET6_ADDRSTRLEN);
#endif
	sprintf(kl->port, "%d", ntohs(((struct sockaddr_in*) &sa)->sin_port));

	kl->next = kliensek;
	kl->fd = ujfd;
	kl->tipp = calloc(tippszam, sizeof(int));
	kl->huzott = calloc(tippszam, sizeof(int));

	kliensek = kl;
	FD_SET(ujfd, &master);
	if (ujfd > fdmax) {
		fdmax = ujfd;
	}
	kliensszam++;
	sprintf(uz, "%d %d\n", tippszam, korlat);
	send(ujfd, uz, strlen(uz), 0);
}

void tipperror(int i) {
	char uz[200];
	memset(uz, '\0', 200);
	strcpy(uz, "\n\ntipperror!\n\n");
	send(i, uz, strlen(uz), 0);
}

void ertekeles(int i) {
	int j;
	struct kliens* kl;
	char uz[200];
	int talalat;
	char s[10];

	memset(uz, '\0', 200);
	strcpy(uz, "\n\nhuzott: ");
	kl = kliensek;
	while (kl->fd != i) {
		kl = kl->next;
	}
	for (j = 0; j < tippszam; ++j) {
		char s[10];
		sprintf(s, "%d,", kl->huzott[j]);
		strcat(uz, s);
	}
	strcat(uz, "\n");
	send(i, uz, strlen(uz), 0);

	strcpy(uz, "\n\ntipp: ");
	for (j = 0; j < tippszam; ++j) {
		char s[10];
		sprintf(s, "%d,", kl->tipp[j]);
		strcat(uz, s);
	}
	strcat(uz, "\n");
	send(i, uz, strlen(uz), 0);
	talalat = 0;
	for (j = 0; j < tippszam; ++j) {
		int k;
		for (k = 0; k < tippszam; ++k) {
			if (kl->tipp[j] == kl->huzott[k]) {
				talalat++;
			}
		}
	}
	strcpy(uz, "\n\ntalalat: ");

	sprintf(s, "%d", talalat);
	strcat(uz, s);
	strcat(uz, "\n");
	send(i, uz, strlen(uz), 0);

}

void statisztika(int i) {
	char uz[200];
	int j;
	sprintf(uz, "Tippelt szamok\n");
	send(i, uz, strlen(uz), 0);
	for (j = 0; j < korlat; ++j) {
		sprintf(uz, "%d--%d \n", j, *(tippeltek + j));
		if (*(tippeltek + j) > 0)
			send(i, uz, strlen(uz), 0);
	}
	sprintf(uz, "Huzott szamok\n");
	send(i, uz, strlen(uz), 0);
	for (j = 0; j < korlat; ++j) {
		sprintf(uz, "%d--%d \n", j, *(huzottak + j));
		if (*(huzottak + j) > 0)
			send(i, uz, strlen(uz), 0);
	}
}

void uzenetfeldolgoz(int i) {
	struct kliens* kl;
	int j = 0;
	int e = 0;
	int* tippek;
	char delim[] = ",";
	char* res = NULL;
	char buff[200];
	int fbyte;

	kl = kliensek;
	while (kl->fd != i) {
		kl = kl->next;
	}

	fbyte = recv(i, buff, sizeof buff, 0);
	if (fbyte <= 0) {
		torol(i);
	} else {
		if (strncmp(buff, "q", 1) == 0) {
			torol(i);
			return;
		}

		if (strncmp(buff, "s", 1) == 0) {
			statisztika(i);
			return;
		}

		tippek = calloc(tippszam, sizeof(int));

		res = strtok(buff, delim);

		while (res != NULL) {
			*(tippek + j) = atoi(res);
			j++;
			res = strtok(NULL, delim);
		}
		if (j != tippszam) {
			tipperror(i);
			return;
		}

		for (j = 0; j < tippszam; ++j) {
			if (*(tippek + j) == 0) {
				e = 1;
			}
		}
		if (e) {
			tipperror(i);
			return;
		}
		memcpy(&kl->tipp, &tippek, tippszam);

		printf("%s:%s \n", kl->ip, kl->port);

		for (j = 0; j < tippszam; ++j) {
			printf("tipp: %d \n", *(kl->tipp + j));
			tippeltek[*(kl->tipp + j)]++;
		}

		for (j = 0; j < tippszam; ++j) {
			*(kl->huzott + j) = (rand() % korlat) + 1;
			huzottak[*(kl->huzott + j)]++;
		}

		for (j = 0; j < tippszam; ++j) {
			printf("huzott: %d \n", *(kl->huzott + j));
		}

		ertekeles(i);

	}
}

int torol(int i) {
	struct kliens* kl;
	struct kliens* elozo;
	int j;
	int ujfdmax = 0;

	if (kliensek->fd == i) {
		kl = kliensek;
		kliensek = kliensek->next;
	} else {
		kl = kliensek->next;
		elozo = kliensek;
		while (kl->fd != i) {
			elozo = kl;
			kl = kl->next;
		}
		elozo->next = kl->next;
	}
	FD_CLR(i, &master);

	for (j = 0; j <= fdmax; ++j) {
		if (FD_ISSET(j,&master)) {
			if (j > ujfdmax) {
				ujfdmax = j;
			}
		}
	}
	printf("torolt: %d\n", kl->fd);
	fflush(stdout);
	fdmax = ujfdmax;
#ifdef _WIN32
	closesocket(i);
#else
	close(i);
#endif
	free(kl);
	kliensszam--;
}

void processloop() {
	int i;
	fd_set readfd;
	int ujfd;
	struct sockaddr sa;
	socklen_t sl = sizeof sa;

	while (1) {
		readfd = master;
		select(fdmax + 1, &readfd, NULL, NULL, NULL);
		for (i = 0; i <= fdmax; ++i) {
			if (FD_ISSET(i,&readfd)) {
				if (i == listenSocket) {
					ujfd = accept(listenSocket, &sa, &sl);
					if (kliensszam < MAXC) {
						elfogad(ujfd, sa);
					} else {
						elutasit(ujfd);
					}
				} else {
					uzenetfeldolgoz(i);
				}
			}
		}
	}
}

void serverlisten() {
	int v;
	struct addrinfo krit;
	struct addrinfo* res;
	memset(&krit, 0, sizeof(struct addrinfo));
	krit.ai_family = AF_UNSPEC;
	krit.ai_socktype = SOCK_STREAM;
	krit.ai_flags = AI_PASSIVE;
	getaddrinfo(NULL, PORT, &krit, &res);
	listenSocket = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
	v = 1;
	setsockopt(listenSocket, SOL_SOCKET, SO_REUSEADDR, &v, sizeof v);
	if (bind(listenSocket, res->ai_addr, res->ai_addrlen)) {
		printf("bind error");
		fflush(stdout);
	}
	listen(listenSocket, MAXC);
	FD_ZERO(&master);
	FD_SET(listenSocket, &master);
	fdmax = listenSocket;
}

int main(int arg, char **argv) {

#ifdef _WIN32
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;

	wVersionRequested = MAKEWORD(2, 2);

	err = WSAStartup(wVersionRequested, &wsaData);
	if (err != 0) {
		printf("WSAStartup failed with error: %d\n", err);
		return 1;
	}
#endif
	srand(time(NULL));

	tippszam = atoi(argv[1]);
	korlat = atoi(argv[2]);

	printf("%d %d\n", tippszam, korlat);
	huzottak = calloc(korlat, sizeof(int));
	tippeltek = calloc(korlat, sizeof(int));

	serverlisten();
	processloop();

#ifdef _WIN32
	WSACleanup();
#endif
	return 0;
}

